﻿//+-------------------------------------------------------------------------------------------------------------------------------------------------------
//
//Description: ViewDefViewModel
//Author: Aravind Ramarathinam (aravr@microsoft.com)
//
//Copyright © Microsoft Corporation
//Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
//You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 
//
//THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, 
//INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. 
//See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
// 
//--------------------------------------------------------------------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Web.Script.Serialization;
using System.Windows.Forms;
using System.Windows.Input;
using VmroleAuthor.Commands;
using VmroleAuthor.Models;

namespace VmroleAuthor.ViewModels
{
    public class ViewDefViewModel : TreeViewItemViewModel, IDataErrorInfo, IProduceValidationResults, ISaveToDisk, IAddViewDefSection, IRemoveChildItem, IJsonOutput
    {
        private readonly ViewDefinition viewDefinition;    
        private readonly VmRoleResourceDefinitionViewModel resourceDefinitionViewModel;      
        private readonly ViewDefResourcesContainerViewModel viewDefResourcesContainerViewModel;

        private readonly ObservableCollection<ViewDefSectionViewModel> initalLoadedViewDefSectionViewModels = new ObservableCollection<ViewDefSectionViewModel>();

        public static string AutoLocalizeMoniker = "[localize this property value]";
        public static string AutoGeneratedLocParamPrefix = "Generated";

        public const string SmallIconFileName = "SmallLogo.png";
        public const string MediumIconFileName = "MediumLogo.png";
        public const string LargeIconFileName = "LargeLogo.png";

        public const int ExpectedSmallIconHeight = 30;
        public const int ExpectedSmallIconWidth = 30;
        public const int ExpectedMediumIconHeight = 60;
        public const int ExpectedMediumIconWidth = 60;
        public const int ExpectedLargeIconHeight = 120;
        public const int ExpectedLargeIconWidth = 120;

        private ViewDefParameterLocalizablePropertyViewModel label;
        private ViewDefParameterLocalizablePropertyViewModel description;
        private ViewDefParameterLocalizablePropertyViewModel publisherLabel;       
      
        private string selectedSmallIconPath;
        private string selectedMediumIconPath;
        private string selectedLargeIconPath;

        private string selectedDefaultLanguageCode;

        public static class PropertyNames
        {
            public const string Summary = "Summary";
            public const string Label = "Label";
            public const string PublisherLabel = "PublisherLabel";
            public const string Description = "Description";
            public const string DefaultLanguageCode = "DefaultLanguageCode";
            public const string Version = "Version";
            public const string SelectedSmallIconPath = "SelectedSmallIconPath";
            public const string SelectedMediumIconPath = "SelectedMediumIconPath";
            public const string SelectedLargeIconPath = "SelectedLargeIconPath";
            public const string SelectedDefaultLanguageCode = "SelectedDefaultLanguageCode";   
        }

        public ViewDefViewModel(ViewDefinition viewDefinition, ResourceDefinitionStorageInfo resourceDefinitionStorageInfo, VmRoleResourceDefinitionViewModel resourceDefinitionViewModel)
            : base(resourceDefinitionViewModel, true)
        {
            this.viewDefinition = viewDefinition;
            this.resourceDefinitionViewModel = resourceDefinitionViewModel;
            this.ResourceDefinitionStorageInfo = resourceDefinitionStorageInfo;
            this.selectedSmallIconPath = this.ResourceDefinitionStorageInfo.SmallIconFileName;
            this.selectedMediumIconPath = this.ResourceDefinitionStorageInfo.MediumIconFileName;
            this.selectedLargeIconPath = this.ResourceDefinitionStorageInfo.LargeIconFileName;


            if (this.viewDefinition == null)
            {
                this.viewDefinition = new ViewDefinition();
            }

            this.SelectSmallIconCommand = new RelayCommand(
            param => this.SelectSmallIcon(),
            param => this.CanSelectIcon());

            this.SelectMediumIconCommand = new RelayCommand(
           param => this.SelectMediumIcon(),
           param => this.CanSelectIcon());

            this.SelectLargeIconCommand = new RelayCommand(
           param => this.SelectLargeIcon(),
           param => this.CanSelectIcon());

            this.UpdateSmallIconCommand = new RelayCommand(
                param => this.UpdateSmallIcon(),
                param => this.CanUpdateIcon());

            this.UpdateMediumIconCommand = new RelayCommand(
                param => this.UpdateMediumIcon(),
                param => this.CanUpdateIcon());

            this.UpdateLargeIconCommand = new RelayCommand(
                param => this.UpdateLargeIcon(),
                param => this.CanUpdateIcon());          
       
            this.ValidationResults = new ObservableCollection<CloudServiceModelValidationResult>();

            this.viewDefResourcesContainerViewModel = new ViewDefResourcesContainerViewModel(this.ResourceDefinitionStorageInfo, this);

            if (this.viewDefinition.Sections != null)
            {
                foreach (ViewDefSection viewDefSection in this.viewDefinition.Sections)
                {
                    var viewDefSectionViewModel = new ViewDefSectionViewModel(viewDefSection, this, this.resourceDefinitionViewModel);
                    this.initalLoadedViewDefSectionViewModels.Add(viewDefSectionViewModel);
                }
            }

            if (this.resourceDefinitionViewModel.IsNewResdefCreatedByTheTool)
            {
                this.Label.PropertyValue = ViewDefViewModel.AutoLocalizeMoniker;
                this.PublisherLabel.PropertyValue = ViewDefViewModel.AutoLocalizeMoniker;
                this.Description.PropertyValue = ViewDefViewModel.AutoLocalizeMoniker;
            }

            this.AutoLoadInitialResdefParametersIfCreatedByThisTool();
        }

        public ResourceDefinitionStorageInfo ResourceDefinitionStorageInfo { get; private set; }

        public ViewDefResourcesContainerViewModel ViewDefResourcesContainerViewModel
        {
            get { return this.viewDefResourcesContainerViewModel; }
        }      

        public string Summary
        {
            get
            {               
                return "ViewDefinition";
            }
        }

        public ViewDefParameterLocalizablePropertyViewModel Label
        {
            get
            {
                if (this.label == null)
                {
                    Func<string> paramValueGetter = () => this.viewDefinition.Label;
                    Action<string> paramValueSetter = delegate(string value) { this.viewDefinition.Label = value; };
                    Func<string> autoParamNameGetter = () => this.resourceDefinitionViewModel.Name + PropertyNames.Label;
                    Func<string> someInitialValue = () => this.resourceDefinitionViewModel.Name;

                    this.label = new ViewDefParameterLocalizablePropertyViewModel(
                        PropertyNames.Label,
                        paramValueGetter,
                        paramValueSetter,
                        autoParamNameGetter,
                        this,
                        true,
                        someInitialValue);
                }
                return this.label;
            }
        }

        public ViewDefParameterLocalizablePropertyViewModel PublisherLabel
        {
            get
            {
                if (this.publisherLabel == null)
                {
                    Func<string> paramValueGetter = () => this.viewDefinition.PublisherLabel;
                    Action<string> paramValueSetter = delegate(string value) { this.viewDefinition.PublisherLabel = value; };
                    Func<string> autoParamNameGetter = () => this.resourceDefinitionViewModel.Publisher + PropertyNames.PublisherLabel;
                    Func<string> someInitialValue = () => this.resourceDefinitionViewModel.Publisher;                   

                    this.publisherLabel = new ViewDefParameterLocalizablePropertyViewModel(
                        PropertyNames.PublisherLabel,
                        paramValueGetter,
                        paramValueSetter,
                        autoParamNameGetter,
                        this,
                        true,
                        someInitialValue);
                }
                return this.publisherLabel;
            }
        }      

        public ViewDefParameterLocalizablePropertyViewModel Description
        {
            get
            {
                if (this.description == null)
                {
                    Func<string> paramValueGetter = () => this.viewDefinition.Description;
                    Action<string> paramValueSetter = delegate(string value) { this.viewDefinition.Description = value; };
                    Func<string> autoParamNameGetter = () => this.resourceDefinitionViewModel.Name + PropertyNames.Description;
                    Func<string> someInitialValue = () => this.resourceDefinitionViewModel.Name;

                    this.description = new ViewDefParameterLocalizablePropertyViewModel(
                        PropertyNames.Description,
                        paramValueGetter,
                        paramValueSetter,
                        autoParamNameGetter,
                        this,
                        true,
                        someInitialValue);
                }
                return this.description;
            }
        }

        public List<string> AvailableDefaultLanguageCodes
        {
            get
            {
                this.viewDefResourcesContainerViewModel.IsExpanded = true;              
                return this.viewDefResourcesContainerViewModel.Children.OfType<ViewDefResourcesViewModel>().Select(viewDefResourcesViewModel => viewDefResourcesViewModel.LanguageCode).ToList();
            }
        }

        public string DefaultLanguageCode
        {
            get { return this.viewDefinition.DefaultLanguageCode; }
            set
            {
                this.viewDefinition.DefaultLanguageCode = value;
                this.OnPropertyChanged(PropertyNames.DefaultLanguageCode);
            }
        }

        public string SelectedDefaultLanguageCode
        {
            get
            {
                return this.selectedDefaultLanguageCode;
            }
            set
            {
                if (value != null)
                {
                    this.DefaultLanguageCode = value;                   
                }
                this.selectedDefaultLanguageCode = value;
                this.OnPropertyChanged(PropertyNames.SelectedDefaultLanguageCode);
                //Note: dont do this here -> this.OnPropertyChanged(DefaultLanguageCode); 
            }
        }


        public string Version
        {
            get { return this.viewDefinition.Version; }
            set
            {
                this.viewDefinition.Version = value;
                this.OnPropertyChanged(PropertyNames.Version);
            }
        }

        #region ICONS

        public string SelectedSmallIconPath
        {
            get { return this.selectedSmallIconPath; }
            set
            {

                this.selectedSmallIconPath = value;
                this.OnPropertyChanged(PropertyNames.SelectedSmallIconPath);
            }
        }
     
        public string SelectedMediumIconPath
        {
            get { return this.selectedMediumIconPath; }
            set
            {

                this.selectedMediumIconPath = value;
                this.OnPropertyChanged(PropertyNames.SelectedMediumIconPath);
            }
        }   

        public string SelectedLargeIconPath
        {
            get { return this.selectedLargeIconPath; }
            set
            {

                this.selectedLargeIconPath = value;
                this.OnPropertyChanged(PropertyNames.SelectedLargeIconPath);
            }
        }    
                  
        public ICommand SelectSmallIconCommand { get; private set; }

        public void SelectSmallIcon()
        {
            string prevpath = this.selectedSmallIconPath;
            this.selectedSmallIconPath = null;
            this.OnPropertyChanged(PropertyNames.SelectedSmallIconPath);  
            var iconPath = this.SelectIcon("30 x 30");
            this.selectedSmallIconPath = !string.IsNullOrEmpty(iconPath) ? iconPath : prevpath;
            this.OnPropertyChanged(PropertyNames.SelectedSmallIconPath);           
        }

        public ICommand SelectMediumIconCommand { get; private set; }

        public void SelectMediumIcon()
        {
            string prevpath = this.selectedMediumIconPath;
            this.selectedMediumIconPath = null;
            this.OnPropertyChanged(PropertyNames.SelectedMediumIconPath);  
            var iconPath = this.SelectIcon("60 x 60");
            this.selectedMediumIconPath = !string.IsNullOrEmpty(iconPath) ? iconPath : prevpath;           
            this.OnPropertyChanged(PropertyNames.SelectedMediumIconPath);
        }

        public ICommand SelectLargeIconCommand { get; private set; }

        public void SelectLargeIcon()
        {
            string prevpath = this.selectedLargeIconPath;
            this.selectedLargeIconPath = null;
            this.OnPropertyChanged(PropertyNames.SelectedLargeIconPath);
            var iconPath = this.SelectIcon("120 x 120");       
            this.selectedLargeIconPath = !string.IsNullOrEmpty(iconPath) ? iconPath : prevpath;
            this.OnPropertyChanged(PropertyNames.SelectedLargeIconPath);          
        }

        public bool CanSelectIcon()
        {
            return this.ResourceDefinitionStorageInfo.IsResdefPkg;
        }

        private string SelectIcon(string titleSize)
        {
            string initialPath = this.ResourceDefinitionStorageInfo.WorkingDir;
            StringBuilder filterSb = new StringBuilder();
            filterSb.Append("Icon file (*.png)|*.png");
            filterSb.Append("|All Files (*.*)|*.*");
            Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog
            {
                DefaultExt = ".*",
                Filter = filterSb.ToString(),
                Title = "Open Icon files: " + titleSize
            };
            if (System.IO.Directory.Exists(initialPath))
            {
                dlg.InitialDirectory = initialPath;
            }
            bool? result = dlg.ShowDialog();
            // Process open file dialog box results 
            if ((result == true) && (dlg.FileName != null))
            {
                return dlg.FileName;
            }
            return null;
        }

        public ICommand UpdateSmallIconCommand { get; private set; }
         
        public ICommand UpdateMediumIconCommand { get; private set; }
       
        public ICommand UpdateLargeIconCommand { get; private set; }

        public bool CanUpdateIcon()
        {
            return this.ResourceDefinitionStorageInfo.IsResdefPkg;
        }

        public void UpdateSmallIcon()
        {
            if (this.UpdateIcon(this.selectedSmallIconPath, SmallIconFileName))
            {
                this.selectedSmallIconPath = Path.Combine(this.ResourceDefinitionStorageInfo.WorkingDir, SmallIconFileName);
                this.ResourceDefinitionStorageInfo.SmallIconFileName = this.selectedSmallIconPath;
            }
            this.OnPropertyChanged(PropertyNames.SelectedSmallIconPath);                  
        }

        public void UpdateMediumIcon()
        {
            if (this.UpdateIcon(this.selectedMediumIconPath, MediumIconFileName))
            {
                this.selectedMediumIconPath = Path.Combine(this.ResourceDefinitionStorageInfo.WorkingDir, MediumIconFileName);
                this.ResourceDefinitionStorageInfo.MediumIconFileName = this.selectedMediumIconPath;
            }
            this.OnPropertyChanged(PropertyNames.SelectedMediumIconPath);
        }

        public void UpdateLargeIcon()
        {
            if (this.UpdateIcon(this.selectedLargeIconPath, LargeIconFileName))
            {
                this.selectedLargeIconPath = Path.Combine(this.ResourceDefinitionStorageInfo.WorkingDir, LargeIconFileName);
                this.ResourceDefinitionStorageInfo.LargeIconFileName = this.selectedLargeIconPath;
            }
            this.OnPropertyChanged(PropertyNames.SelectedLargeIconPath);
        }

        private bool UpdateIcon(string sourceIconPath,  string targetFileName)
        {
            // Basic checks before applyin
            string error = this.ValidateBasicRequirementsForSelectedIconPath();
            if (error != null)
            {
                MessageBox.Show("Error: " + error, DefinitionViewModel.ToolHeader);
                this.OnPropertyChanged(PropertyNames.SelectedSmallIconPath);     
                this.OnPropertyChanged(PropertyNames.SelectedMediumIconPath);  
                this.OnPropertyChanged(PropertyNames.SelectedLargeIconPath);  
                return false;
            }

            //Is Relative
            if (this.IsSelectedIconARelativePath(sourceIconPath))
            {
                return this.ProcessUpdateForSelectedRelativePath(sourceIconPath, targetFileName);
            }
            //this in turn copies to working dir and processes update for relative path
            return this.ProcessUpdateForSelectedAbsolutePath(sourceIconPath, targetFileName);
        }


        private bool ProcessUpdateForSelectedRelativePath(string sourceIconPath,  string targetFileName)
        {
            string resolvedfullPath = Path.Combine(this.ResourceDefinitionStorageInfo.WorkingDir, sourceIconPath);
            if (!File.Exists(resolvedfullPath))
            {
                string fmtError = string.Format(
                    "No icon with relative path {0} exists in resdef package working dir {1}",
                    sourceIconPath, this.ResourceDefinitionStorageInfo.WorkingDir);
                MessageBox.Show(fmtError, DefinitionViewModel.ToolHeader);
                return false;
            }

            string targetfullPath = Path.Combine(this.ResourceDefinitionStorageInfo.WorkingDir, targetFileName);
            if (string.Compare(resolvedfullPath, targetfullPath, StringComparison.OrdinalIgnoreCase) != 0)
            {
                try
                {
                    File.Move(resolvedfullPath, targetfullPath);
                }
                catch (Exception ex)
                {
                    if (MessageBox.Show("Can copy over? Unable to rename, so delete this out of band: " + ex.Message, DefinitionViewModel.ToolHeader, MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
                    {
                        try
                        {
                            System.IO.File.Copy(resolvedfullPath, targetfullPath, true);            
                        }
                        catch (Exception ex2)
                        {
                            MessageBox.Show("Failed to Copy the icon to the target location. Error: " + ex2.Message, DefinitionViewModel.ToolHeader);
                        }                        
                    }                
                }                
            }

            return true;            
        }

        private bool ProcessUpdateForSelectedAbsolutePath(string sourceIconPath, string targetFileName)
        {
            string filename = System.IO.Path.GetFileName(sourceIconPath);
            if (string.IsNullOrEmpty(filename))
            {
                MessageBox.Show("Incorrect filename specified", DefinitionViewModel.ToolHeader);
                return false;
            }
          
            //is abs path referrign to folder under working folder
            string resolvedTargetFullPath = Path.Combine(this.ResourceDefinitionStorageInfo.WorkingDir, filename);
            if (String.Compare(Path.GetFullPath(sourceIconPath).TrimEnd('\\'),
                               Path.GetFullPath(resolvedTargetFullPath).TrimEnd('\\'),
                               StringComparison.InvariantCultureIgnoreCase) == 0)
            {
                return this.ProcessUpdateForSelectedRelativePath(sourceIconPath, targetFileName);             
            }
          
            //this is someother icon in a different directory. this must be copied to data package dir location
            if (!File.Exists(sourceIconPath))
            {
                MessageBox.Show("Source Icon does not exist @:" + sourceIconPath, DefinitionViewModel.ToolHeader);
                return false;
            }

            if (File.Exists(resolvedTargetFullPath))
            {
                // Can overwrite target package?
                if (MessageBox.Show("Can overwrite icon", DefinitionViewModel.ToolHeader, MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.No)
                {
                    //do no stuff
                    return false;
                }
            }
            //copy 
            string updatedReslovedTargetFullPath = Path.Combine(this.ResourceDefinitionStorageInfo.WorkingDir, targetFileName);
            // overwrite the destination file if it already exists.
            System.IO.File.Copy(sourceIconPath, updatedReslovedTargetFullPath, true);            
            // get relative path and process update for selected relative path           
            // its now copied with that path, so source is the same targetFileName
            return true;
        }      

        private string ValidateBasicRequirementsForSelectedIconPath()
        {
            string error;
            if (this.ResourceDefinitionStorageInfo == null)
            {
                error = "Disk storage info such as working package dir is not set";
                return error;
            }

            if (!Directory.Exists(this.ResourceDefinitionStorageInfo.WorkingDir))
            {
                error = "Working package dir does not exist: " + this.ResourceDefinitionStorageInfo.WorkingDir;
                return error;
            }           
            return null;
        }

        private bool IsSelectedIconARelativePath(string sourceIconPath)
        {
            return !Path.IsPathRooted(sourceIconPath);
        }      

        #endregion ICONS
      

        public List<string> AvailableLocalizedPropertyValues
        {
            get
            {
                //TODO:
                List<string> availableLocalizedPropertyValues = new List<string>();
                availableLocalizedPropertyValues.Add(AutoLocalizeMoniker);
                return availableLocalizedPropertyValues;
            }
        }

        public bool ContainsLocalizableParameter(string parameterName)
        {
            // across all languages
            return this.viewDefResourcesContainerViewModel.ContainsLocalizableParameter(parameterName);          
        }

        public void AddLocalizableParameterIfNotExist(string parameterName, Func<string> optionalInitialValueGetter = null)
        {
            this.viewDefResourcesContainerViewModel.AddLocalizableParameterIfNotExist(parameterName, optionalInitialValueGetter);         
        }

        public void RemoveLocalizableParameterIExists(string parameterName)
        {
            this.viewDefResourcesContainerViewModel.RemoveLocalizableParameterIExists(parameterName);          
        }

        public string PrettyJson
        {
            get { return this.GetPrettyJson(false); }
        }

        public string GetRawJson(bool popOnError)
        {
             try
            {
                JavaScriptSerializer ser = new JavaScriptSerializer();
                ser.RegisterConverters(new JavaScriptConverter[] { new NullPropertiesConverter() });
                ser.RegisterConverters(new JavaScriptConverter[] { new ViewDefOptionValueJsonConverter() });                
                return ser.Serialize(this.viewDefinition);                   
            }
            catch (Exception ex)
            {
                string error = string.Format(
                    "Unable to serialize view definition {0}. Exception details : {1}",
                    this.resourceDefinitionViewModel.Name ?? "Unspecified name",
                    ex.Message);
                if (popOnError)
                {
                    System.Windows.MessageBox.Show(error, DefinitionViewModel.ToolHeader);
                }
                return error;
            }          
        }


        public string GetPrettyJson(bool popOnError)
        {
            return JsonHelper.PrettyPrentJson(this.GetRawJson(popOnError));          
        }

        public void SaveToDisk()
        {
            if (this.ResourceDefinitionStorageInfo != null)
            {
                try
                {
                    using (StreamWriter outfile = new StreamWriter
                       (new FileStream(this.ResourceDefinitionStorageInfo.ViewDefFileName, FileMode.Create, FileAccess.ReadWrite),
                       Encoding.UTF8))
                    {
                        outfile.Write(this.GetPrettyJson(true));
                    }                      
                }
                catch (Exception ex)
                {
                    MessageBox.Show(string.Format("Unable to save viewdef json in package. File save path {0}. Exception details : {1}", this.ResourceDefinitionStorageInfo.ViewDefFileName, ex.Message), "Save VmRole Resource Definition Package");
                }
             
                this.viewDefResourcesContainerViewModel.SaveToDisk();            
            }
            else
            {
                MessageBox.Show(string.Format("Unable to save viewdef json and resources in package. Storage Info not set"), "Save VmRole Resource Definition Package");
            }
        }

        public bool CanAddViewDefSection
        {
            get
            {
                return true;
            }
        }

        public void AddViewDefSection()
        {
            this.IsExpanded = true; //critical to be done first            
            Debug.Assert(this.viewDefinition != null);
            ViewDefSection section = new ViewDefSection();    
            if (this.viewDefinition.Sections == null)
            {
                this.viewDefinition.Sections = new List<ViewDefSection>();
            }
            this.viewDefinition.Sections.Add(section);
            var viewDefSectionViewModel = new ViewDefSectionViewModel(section, this, this.resourceDefinitionViewModel);
            this.Children.Add(viewDefSectionViewModel);
            viewDefSectionViewModel.IsSelected = true;
        }

        protected override void LoadChildren()
        {            
            // Load sections
            foreach (ViewDefSectionViewModel initalLoadedViewDefSectionViewModel in this.initalLoadedViewDefSectionViewModels)
            {
                this.Children.Add(initalLoadedViewDefSectionViewModel);
            }

            // Load Resources
            this.Children.Add(this.viewDefResourcesContainerViewModel);
        }

        public void RemoveChild(TreeViewItemViewModel childItem)
        {
            var viewDefSectionViewModel = childItem as ViewDefSectionViewModel;
            if (viewDefSectionViewModel != null)
            {
                viewDefSectionViewModel.Cleanup();
                if (this.viewDefinition.Sections != null)
                {
                    this.viewDefinition.Sections.Remove(viewDefSectionViewModel.ViewDefSection);
                }
                this.Children.Remove(childItem);
            }          
        }


        public void UpdateOsvhdRequirementTags(List<string> tagsToBeRemoved, List<string> tagsToBeAdded)
        {
            this.IsExpanded = true;
            foreach (var childItem in this.Children)
            {
                var viewDefSectionViewModel = childItem as ViewDefSectionViewModel;
                if (viewDefSectionViewModel != null)
                {
                    viewDefSectionViewModel.IsExpanded = true;
                    foreach (var childCategory in viewDefSectionViewModel.Children)
                    {
                        var viewDefCategoryViewModel = childCategory as ViewDefCategoryViewModel;
                        if (viewDefCategoryViewModel != null)
                        {
                            foreach (var viewDefParameterViewModel in viewDefCategoryViewModel.ViewDefParameterViewModels)
                            {
                                ViewDefParameterType paramType;
                                if (EnumHelper.TryParse(viewDefParameterViewModel.Type, true, out paramType) &&
                                    paramType == ViewDefParameterType.OSVirtualHardDisk)
                                {
                                    if (tagsToBeRemoved != null)
                                    {
                                        foreach (string removeTag in tagsToBeRemoved)
                                        {
                                            viewDefParameterViewModel.RemoveTag(removeTag);
                                        }
                                    }
                                    if (tagsToBeAdded != null)
                                    {
                                        foreach (string addTag in tagsToBeAdded)
                                        {
                                            if (viewDefParameterViewModel.CanAddTag(addTag))
                                            {
                                                viewDefParameterViewModel.AddTag(addTag);
                                            }                                            
                                        }  
                                    }                                                                  
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

        public void ClearOsvhdRequirementTags()
        {
            this.IsExpanded = true;
            foreach (var childItem in this.Children)
            {
                var viewDefSectionViewModel = childItem as ViewDefSectionViewModel;
                if (viewDefSectionViewModel != null)
                {
                    viewDefSectionViewModel.IsExpanded = true;
                    foreach (var childCategory in viewDefSectionViewModel.Children)
                    {
                        var viewDefCategoryViewModel = childCategory as ViewDefCategoryViewModel;
                        if (viewDefCategoryViewModel != null)
                        {
                            foreach (var viewDefParameterViewModel in viewDefCategoryViewModel.ViewDefParameterViewModels)
                            {
                                ViewDefParameterType paramType;
                                if (EnumHelper.TryParse(viewDefParameterViewModel.Type, true, out paramType) &&
                                    paramType == ViewDefParameterType.OSVirtualHardDisk)
                                {
                                    viewDefParameterViewModel.ViewDefParameter.ImageTags.Clear();
                                    viewDefParameterViewModel.ImageTags.Clear();                                  
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }


        public void RemoveApplicationSettingsViewDefSectionOnResExtUnbind()
        {
            this.IsExpanded = true;
            foreach (var childItem in this.Children)
            {
                var viewDefSectionViewModel = childItem as ViewDefSectionViewModel;
                if (viewDefSectionViewModel != null && viewDefSectionViewModel.IsThisApplicationSettingsAutoCreatedByTool)
                {   
                    bool hasParameters = false;
                    if (viewDefSectionViewModel.ViewDefSection.Categories != null)
                    {
                        if (viewDefSectionViewModel.ViewDefSection.Categories.Any(category => category.Parameters != null && category.Parameters.Count > 0))
                        {
                            hasParameters = true;
                        }
                    }
                    if (!hasParameters)
                    {
                        this.viewDefinition.Sections.Remove(viewDefSectionViewModel.ViewDefSection);
                        this.Children.Remove(childItem);
                    }
                    break;
                }
            }          
        }
   

        public List<string> GetOsVhdRequirementsImageTags()
        {
            this.IsExpanded = true;
            foreach (var childItem in this.Children)
            {
                var viewDefSectionViewModel = childItem as ViewDefSectionViewModel;
                if (viewDefSectionViewModel != null)
                {
                    viewDefSectionViewModel.IsExpanded = true;
                    foreach (var childCategory in viewDefSectionViewModel.Children)
                    {
                        var viewDefCategoryViewModel = childCategory as ViewDefCategoryViewModel;
                        if (viewDefCategoryViewModel != null)
                        {
                            foreach (var viewDefParameterViewModel in viewDefCategoryViewModel.ViewDefParameterViewModels)
                            {
                                ViewDefParameterType paramType;
                                if (EnumHelper.TryParse(viewDefParameterViewModel.Type, true, out paramType) &&
                                    paramType == ViewDefParameterType.OSVirtualHardDisk)
                                {
                                    return viewDefParameterViewModel.ViewDefParameter.ImageTags;
                                }
                            }
                        }
                    }
                }
            }
            return null;
        }


        public void AddViewDefParameter(ParameterViewModel parameterViewModel)
        {
            if (string.IsNullOrEmpty(parameterViewModel.Name))
            {
                return;
            }
            ViewDefCategoryViewModel viewDefCategoryViewModelToAdd;
            if (parameterViewModel.ViewDefParamInfo.ResourceExtensionParameterReferenedInResourceDefinition ||
                parameterViewModel.Description.IndexOf("Resource Extension", StringComparison.OrdinalIgnoreCase) > -1)
            {
                viewDefCategoryViewModelToAdd = GetBestEstimateOfApplicationSettingsCategoryToAutoAdd();
                if ((viewDefCategoryViewModelToAdd == null))
                {
                    //Try adding it
                    var section = new ViewDefSection();
                    section.Title = ViewDefSectionViewModel.ApplicationSettingsAutoCreatedByToolMoniker;
                    Debug.Assert(this.viewDefinition != null);   
                    this.viewDefinition.Sections.Add(section);
                    section.Categories = new List<ViewDefCategory>();
                    var category = new ViewDefCategory();
                    category.CategoryName = ViewDefCategoryViewModel.ApplicationCategoryAutoCreatedByToolMoniker;
                    section.Categories.Add(category);

                    this.IsExpanded = true; //critical to be done first                               
                    var viewDefSectionViewModel = new ViewDefSectionViewModel(section, this, this.resourceDefinitionViewModel);
                    this.Children.Insert(this.Children.Count - 1, viewDefSectionViewModel);
                    //this.Children.Add(viewDefSectionViewModel);

                    viewDefCategoryViewModelToAdd = GetBestEstimateOfApplicationSettingsCategoryToAutoAdd();
                    
                }
            }
            else
            {
                viewDefCategoryViewModelToAdd = GetBestEstimateOfVMSettingsCategoryToAutoAdd();
            }

            if (viewDefCategoryViewModelToAdd != null)
            {
                viewDefCategoryViewModelToAdd.AddViewDefParameter(parameterViewModel, true);
            }
        }


        private ViewDefCategoryViewModel GetBestEstimateOfVMSettingsCategoryToAutoAdd()
        {
            this.IsExpanded = true;
            foreach (var childItem in this.Children)
            {
                var viewDefSectionViewModel = childItem as ViewDefSectionViewModel;
                if (viewDefSectionViewModel != null)
                {
                    string title = viewDefSectionViewModel.Title.PropertyValue ?? string.Empty;
                    if (viewDefSectionViewModel.IsThisVMSettingsAutoCreatedByTool ||
                        title.IndexOf("VM", StringComparison.OrdinalIgnoreCase) > -1 ||
                        title.IndexOf("Machine", StringComparison.OrdinalIgnoreCase) > -1
                        )
                    {
                        viewDefSectionViewModel.IsExpanded = true;
                        foreach (var childCategory in viewDefSectionViewModel.Children)
                        {
                            var viewDefCategoryViewModel = childCategory as ViewDefCategoryViewModel;
                            if (viewDefCategoryViewModel != null)
                            {
                                return viewDefCategoryViewModel;
                            }
                        }                       
                    }                   
                }
            }
            return null;
        }

        private ViewDefCategoryViewModel GetBestEstimateOfApplicationSettingsCategoryToAutoAdd()
        {
            this.IsExpanded = true;
            foreach (var childItem in this.Children)
            {
                var viewDefSectionViewModel = childItem as ViewDefSectionViewModel;
                if (viewDefSectionViewModel != null)
                {
                    string title = viewDefSectionViewModel.Title.PropertyValue ?? string.Empty;
                    if (viewDefSectionViewModel.IsThisApplicationSettingsAutoCreatedByTool ||
                        title.IndexOf("VMSettings", StringComparison.OrdinalIgnoreCase) == -1 )                  
                    {
                        viewDefSectionViewModel.IsExpanded = true;
                        foreach (var childCategory in viewDefSectionViewModel.Children)
                        {
                            var viewDefCategoryViewModel = childCategory as ViewDefCategoryViewModel;
                            if (viewDefCategoryViewModel != null)
                            {
                                return viewDefCategoryViewModel;
                            }
                        }
                    }
                }
            }
            return null;
        }


        public void RemoveViewDefParameter(string paramName, ViewDefParameterViewModel doNotRemoveThisFromTheCategory = null)
        {
            this.IsExpanded = true;
            foreach (var childItem in this.Children)
            {
                var viewDefSectionViewModel = childItem as ViewDefSectionViewModel;
                if (viewDefSectionViewModel != null)
                {
                    viewDefSectionViewModel.IsExpanded = true;
                    foreach (var childCategory in viewDefSectionViewModel.Children)
                    {
                        var viewDefCategoryViewModel = childCategory as ViewDefCategoryViewModel;
                        if (viewDefCategoryViewModel != null)
                        {
                            for (int i =0; i < viewDefCategoryViewModel.ViewDefParameterViewModels.Count; ++i)
                            {
                                var viewDefParameterViewModel = viewDefCategoryViewModel.ViewDefParameterViewModels[i];
                                if (
                                    (viewDefParameterViewModel != doNotRemoveThisFromTheCategory) &&
                                    (string.Equals(viewDefParameterViewModel.Name, paramName, StringComparison.Ordinal))
                                    )
                                {
                                    viewDefCategoryViewModel.ViewDefParameterViewModels[i].RemoveViewDefParameter();                                                                        
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }


        public ViewDefParameter GetViewDefParameter(string paramName)
        {
            if (string.IsNullOrEmpty(paramName))
            {
                return null;
            }
            if (this.viewDefinition.Sections != null)
            {
                foreach (var viewDefSection in this.viewDefinition.Sections)
                {
                    if (viewDefSection != null && viewDefSection.Categories != null)
                    {
                        foreach (var viewDefCategory in viewDefSection.Categories)
                        {
                            if (viewDefCategory.Parameters != null)
                            {
                                foreach (ViewDefParameter viewDefParameter in viewDefCategory.Parameters)
                                {
                                    if (viewDefParameter != null && string.Equals(viewDefParameter.Name, paramName, StringComparison.Ordinal))
                                    {
                                        return viewDefParameter;
                                    }
                                }
                            }
                        }
                    }
                }
                //return (from section in this.viewDefinition.Sections where section.Categories != null from category in section.Categories where category.Parameters != null from param in category.Parameters select param).FirstOrDefault(param => string.Equals(param.Name, paramName, StringComparison.Ordinal));
            }         
            return null;
        }


        public bool ParameterExistsInViewDef(string paramName)
        {
            if (string.IsNullOrEmpty(paramName))
            {
                return false;
            }
            if (this.viewDefinition.Sections != null)
            {
                return this.viewDefinition.Sections.Where(section => section.Categories != null).Any(section => section.Categories.Where(category => category.Parameters != null).Any(category => category.Parameters.Where(viewDefParam => viewDefParam != null && viewDefParam.Name != null).Any(viewDefParam => paramName.Equals(viewDefParam.Name, StringComparison.Ordinal))));
            }
            return false;
        }

        private void AutoLoadInitialResdefParametersIfCreatedByThisTool()
        {
            if (!this.resourceDefinitionViewModel.IsNewResdefCreatedByTheTool)
            {
                return;
            }
            
            ViewDefCategoryViewModel viewDefCategoryViewModelToAdd = null;
            foreach (ViewDefSectionViewModel initalLoadedViewDefSectionViewModel in initalLoadedViewDefSectionViewModels)
            {
                if (initalLoadedViewDefSectionViewModel.IsThisVMSettingsAutoCreatedByTool)
                {               
                    foreach (var initialLoadedCategoryViewModel in initalLoadedViewDefSectionViewModel.InitialLoadedCategoryViewModels)
                    {
                        if (initialLoadedCategoryViewModel.IsThisVMCategoryAutoCreatedByTool)
                        {
                            viewDefCategoryViewModelToAdd = initialLoadedCategoryViewModel;
                            break;
                        }                       
                    }
                    break;
                }
            }

            Debug.Assert(viewDefCategoryViewModelToAdd != null);
            if (viewDefCategoryViewModelToAdd != null)
            {
                foreach (var parameterViewModel in this.resourceDefinitionViewModel.ResourceDefinitionParametersViewModel.Parameters)
                {
                    viewDefCategoryViewModelToAdd.AddViewDefParameter(parameterViewModel, true);
                }
            }            
        }          

        public string this[string propertyName]
        {
            get
            {              
                switch (propertyName)
                {
                    case PropertyNames.Version:
                        return ValidationHelper.ValidateVersionFormat(this.Version, false);                       
                    case PropertyNames.SelectedSmallIconPath:
                        return ValidationHelper.ValidateIcon(this.selectedSmallIconPath, this.ResourceDefinitionStorageInfo.WorkingDir, SmallIconFileName, ExpectedSmallIconHeight, ExpectedSmallIconWidth);
                    case PropertyNames.SelectedMediumIconPath:
                        return ValidationHelper.ValidateIcon(this.selectedMediumIconPath, this.ResourceDefinitionStorageInfo.WorkingDir, MediumIconFileName, ExpectedMediumIconHeight, ExpectedMediumIconWidth);
                    case PropertyNames.SelectedLargeIconPath:
                        return ValidationHelper.ValidateIcon(this.selectedLargeIconPath, this.ResourceDefinitionStorageInfo.WorkingDir, LargeIconFileName, ExpectedLargeIconHeight, ExpectedLargeIconWidth);
                    case PropertyNames.DefaultLanguageCode:
                        if (!this.AvailableDefaultLanguageCodes.Contains(this.DefaultLanguageCode))
                        {
                            return string.Format("Specified Default Language code {0} is not valid as there is no localized resources file under that language code in this package",  this.DefaultLanguageCode);
                        }
                        break;                    
                }
                return null;
            }
        }

        public string Error { get; set; }

        public ObservableCollection<CloudServiceModelValidationResult> ValidationResults { get; private set; }

        public void UpdateValidationResults()
        {
            this.IsExpanded = true;
            ModelValidationResultsHelper.UpdateValidationResults(this, typeof(PropertyNames));
            //Add any other cross-field validations error           
        }     
    }
}
